Skip to content

[TRTLLM-13233][feat] Support no_repeat_ngram_size in TorchSampler and refactor token-ban handling into its own submodule#16594

Open
zhaoyangwang-nvidia wants to merge 1 commit into
NVIDIA:mainfrom
zhaoyangwang-nvidia:torch-sampler-no-repeat-ngram
Open

[TRTLLM-13233][feat] Support no_repeat_ngram_size in TorchSampler and refactor token-ban handling into its own submodule#16594
zhaoyangwang-nvidia wants to merge 1 commit into
NVIDIA:mainfrom
zhaoyangwang-nvidia:torch-sampler-no-repeat-ngram

Conversation

@zhaoyangwang-nvidia

@zhaoyangwang-nvidia zhaoyangwang-nvidia commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

Summary

Dev Engineer Review

  • Added no_repeat_ngram_size support to TorchSampler, aligning with C++ banRepeatNgram and Hugging Face behavior:
    • Prevents sampling tokens that would recreate an existing n-gram in the generated sequence including prompt tokens.
    • Enforced per beam.
    • When n == 1, bans all previously seen tokens (unigram repeat prevention).
    • Feature is disabled for None or 0.
  • Refactored bad-word and no-repeat-ngram logic to use a shared suffix-rule ban framework:
    • Unified suffix-rule ban collection with a single shared flush via _apply_token_bans.
    • Added memoized token/context access to reduce host overhead.
    • Introduced _TokenBans to accumulate ban targets across unconditional, overlap-conditioned, and device-token-column cases.
    • Implemented incremental per-request n-gram state indexing via _NgramIndexCache for efficient updates.
    • Reused the existing neg_inf tensor for overlap conditional penalties to avoid extra allocations.
    • Updated _process_requests to compute per-request ngram_sizes and apply both features via shared collection/flush when overlap scheduler tracking is enabled.
    • Improved cache lifecycle by storing _NgramIndexCache in WeakKeyDictionary[LlmRequest, _NgramIndexCache] so state is reclaimed when requests are freed (including rollback scenarios).
  • Updated request plumbing:
    • executor_request_to_llm_request now reads executor_request.sampling_config.no_repeat_ngram_size, normalizes falsy/disabled values to None, and sets llm_request.py_no_repeat_ngram_size.
  • Documentation/API clarity:
    • Updated docs/source/features/sampling.md to document prompt-inclusive n-gram banning and that None/0 disables the restriction.
    • Updated SamplingParams docstring to explicitly describe the prompt-inclusive behavior and the default (None).

QA Engineer Review

  • Test code changes (CUDA unit tests) in tests/unittest/_torch/sampler/test_torch_sampler.py:
    • Modified TestApplyBadWords to use a new sampler helper for both fresh and stale-host paths.
    • Added CUDA-only TestApplyNoRepeatNgram with a MockLlmRequest and helpers to validate banned-logit columns and correct index/cache behavior.
    • Added 18 test cases covering: matching behavior, disabled requests, speculative decoding, multi-beam layouts, overlap handling, duplicate bans (no NaNs), incremental cache updates, cache growth/rollback invalidation, and stale-host execution paths (device hit/miss, mixed stale/fresh batches, window ending at device token).
  • Integration/manual coverage in tests/integration/test_lists/, test-db/, or qa/: not verifiable from the provided context (no test-list changes available).
    • Verdict: needs follow-up.

Description

This PR does two things:

  1. Implements no_repeat_ngram_size for the PyTorch TorchSampler. The
    parameter was previously honored only by the C++ executor path and silently
    ignored on the PyTorch backend.
  2. Refactors all token-ban handling (bad words, no-repeat ngram, and the
    min-length EOS suppression) out of the large sampler.py into a dedicated
    token_ban submodule with a small, testable class hierarchy.

no_repeat_ngram_size semantics

A token is excluded from sampling if it would recreate an n-gram already
present in the sequence (prompt included) — same semantics as the C++
banRepeatNgram kernel and HF's NoRepeatNGramLogitsProcessor. n == 1 bans
every token already present; the restriction is per-beam. None or 0
disables it. Docs (docs/source/features/sampling.md) and the SamplingParams
docstring are updated accordingly.

Token-ban submodule (token_ban.py)

Bad words, no-repeat ngram, and min-length EOS suppression are all the same
shape of operation — mask specific logits to -inf. They now share one module:

  • TokenBans — host-side accumulator of (row, col) index lists. Every
    feature appends into a single instance, so all bans reach the device with one
    H2D transfer + one index_put_ per category (instead of one set per feature).
  • TokenBanHandler (ABC) — holds the shared feature logic: the bad-words /
    no-repeat-ngram rule generators, the suffix-rule matcher, the incremental
    per-request n-gram index, min-length collection, and the unconditional flush.
  • Two variants, selected once at construction from whether the overlap
    scheduler is enabled (a fixed config choice, not per-request):
    • SynchronousTokenBanHandler — overlap off; the host history is always
      complete, so every ban is unconditional.
    • OverlappedTokenBanHandler — overlap on; a batch mixes fresh requests
      (history complete) with stale ones (missing the previous step's token,
      still on the device). Stale requests produce conditional bans resolved on
      the device at apply time (torch.where on the pending token) without a
      device-to-host sync
      .

TorchSampler now just owns a handler and calls
generate_ban_list() / apply_ban_list(); it computes the per-request stale
flags since it owns the pending-step / draft-batch context.

Performance

The n-gram matcher keeps an incremental per-request index rather than
rescanning the history each step: a host-side token mirror is grown via the
scalar get_num_tokens / get_last_tokens accessors, avoiding the ~40 µs full
get_tokens() copy, so the steady-state per-step cost is O(new tokens).

Isolated sampler benchmark (H200, bs=32, 2048-token context per request, n=3,
p50 sampler-beyond-forward):

p50 overhead vs baseline
baseline (feature off) ~124 µs
no_repeat_ngram enabled ~126 µs ~2 µs

The submodule refactor and folding in min-length introduced no measurable
change vs the pre-refactor numbers.

Overlap + speculation / beam search

As with bad words, when the overlap scheduler is combined with speculative
decoding or beam search the missing host tokens cannot be reconstructed with
the single-token device check, so bans may be enforced one step late (warned
once). The exact stale path only runs for single-step, single-beam requests.

Test Coverage

tests/unittest/_torch/sampler/test_token_ban.py (new file) covers all three
token-ban paths against the handlers directly:

  • Bad words — single-/multi-token words, prefix hit/miss, prompt-internal
    prefixes, and the full stale-host overlap matrix.
  • No-repeat ngram — bigram/trigram hit & miss, multiple matches, n == 1,
    sequences shorter than n, disabled (None and 0), multi-step and
    multi-beam (per-beam history) layouts, the stale overlap matrix (device
    hit/miss, window ending at the device token, mixed stale+fresh, no NaN on
    duplicate bans), incremental cache growth, and cache rebuild on rollback.
  • Min length — EOS suppressed below the minimum (generated length excludes
    the prompt), no ban once reached, invalid end_id skipped, multi-step boundary.

Tests assert that logits outside the banned set are left unchanged (starting
from distinct non-zero logits), not just that the banned cells become -inf.

All pass on H200. Additional randomized fuzz (matched against a brute-force
reference and against the "fresh path on the completed sequence" for the stale
path) was run during development.

PR Checklist

  • PR title follows the [JIRA/NVBUG/None][type] description format
  • Documentation updated (docs/source/features/sampling.md, SamplingParams docstring)
  • Unit tests added and passing
  • pre-commit hooks pass

PR Checklist

Please review the following before submitting your PR:

  • PR description clearly explains what and why. If using CodeRabbit's summary, please make sure it makes sense.

  • PR Follows TRT-LLM CODING GUIDELINES to the best of your knowledge.

  • Test cases are provided for new code paths (see test instructions)

  • If PR introduces API changes, an appropriate PR label is added - either api-compatible or api-breaking. For api-breaking, include BREAKING in the PR title.

  • Any new dependencies have been scanned for license and vulnerabilities

  • CODEOWNERS updated if ownership changes

  • Documentation updated as needed

  • Update tava architecture diagram if there is a significant design change in PR.

  • The reviewers assigned automatically/manually are appropriate for the PR.

  • Please check this after reviewing the above items as appropriate for this PR.

GitHub Bot Help

To see a list of available CI bot commands, please comment /bot help.

@zhaoyangwang-nvidia zhaoyangwang-nvidia changed the title [TRTLLM-13233][feat] Support no_repeat_ngram_size in TorchSampler [None][feat] Support beam search length_penalty/diversity_rate and validate early_stopping in TorchSampler Jul 21, 2026
@zhaoyangwang-nvidia zhaoyangwang-nvidia changed the title [None][feat] Support beam search length_penalty/diversity_rate and validate early_stopping in TorchSampler [13233][feat] Support no_repeat_ngram_size in TorchSampler Jul 21, 2026
@zhaoyangwang-nvidia
zhaoyangwang-nvidia marked this pull request as ready for review July 21, 2026 08:03
@zhaoyangwang-nvidia
zhaoyangwang-nvidia requested review from a team as code owners July 21, 2026 08:03
@zhaoyangwang-nvidia zhaoyangwang-nvidia changed the title [13233][feat] Support no_repeat_ngram_size in TorchSampler [TRTLLM-13233][feat] Support no_repeat_ngram_size in TorchSampler Jul 21, 2026
@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

The Torch executor now propagates no_repeat_ngram_size into LLM requests. The Torch sampler combines no-repeat n-gram and bad-word bans, handles stale overlap-scheduler context, and adds CUDA coverage for fresh and stale token histories.

Changes

No-repeat n-gram sampling

Layer / File(s) Summary
Sampling parameter propagation
tensorrt_llm/_torch/pyexecutor/llm_request.py, tensorrt_llm/sampling_params.py, docs/source/features/sampling.md
Request conversion stores disabled values as None; documentation describes prompt-inclusive n-gram matching and None/0 disabling.
Unified token-ban collection
tensorrt_llm/_torch/pyexecutor/sampler/sampler.py
Memoized context and _TokenBans support combined bad-word and no-repeat n-gram collection, including overlap-scheduler stale-context handling and weakly keyed n-gram caches.
Fresh and stale-context validation
tests/unittest/_torch/sampler/test_torch_sampler.py
CUDA tests cover n-gram sizes, cache changes, disabled requests, multi-step rows, stale device tokens, mixed batches, and duplicate bans.

Estimated code review effort: 4 (Complex) | ~45 minutes

Suggested reviewers: arysef

Sequence Diagram(s)

sequenceDiagram
  participant ExecutorRequest
  participant LlmRequest
  participant TorchSampler
  participant Logits
  ExecutorRequest->>LlmRequest: propagate no_repeat_ngram_size
  LlmRequest->>TorchSampler: provide request context and ngram size
  TorchSampler->>TorchSampler: collect bad-word and no-repeat n-gram bans
  TorchSampler->>Logits: apply accumulated token bans
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title follows the required ticket-and-type format and accurately summarizes the main change.
Description check ✅ Passed The description includes the required Description, Test Coverage, and PR Checklist sections and gives concrete implementation and testing details.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tests/unittest/_torch/sampler/test_torch_sampler.py (1)

2789-2797: 📐 Maintainability & Code Quality | 🔵 Trivial

Consider adding a multi-beam coverage case.

All _run/_run_stale invocations use num_beams == 1, so the multi-beam branch of _collect_no_repeat_ngram_bans.fresh_rules (the num_beams[index] != 1 path that scans each beam's context directly rather than the incremental index) is never exercised. Since beam search is a supported production path, a follow-up test that passes num_beams > 1 with divergent per-beam histories would close this gap.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unittest/_torch/sampler/test_torch_sampler.py` around lines 2789 -
2797, Add a focused multi-beam test using the existing _run or _run_stale
helpers, passing num_beams greater than one with distinct histories for each
beam. Assert the no-repeat n-gram bans for every beam, exercising
_collect_no_repeat_ngram_bans.fresh_rules through its num_beams[index] != 1 path
while preserving existing single-beam coverage.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tests/unittest/_torch/sampler/test_torch_sampler.py`:
- Around line 2789-2797: Add a focused multi-beam test using the existing _run
or _run_stale helpers, passing num_beams greater than one with distinct
histories for each beam. Assert the no-repeat n-gram bans for every beam,
exercising _collect_no_repeat_ngram_bans.fresh_rules through its
num_beams[index] != 1 path while preserving existing single-beam coverage.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 8beb9dee-254c-4001-aea5-a5e5f79b7bfe

📥 Commits

Reviewing files that changed from the base of the PR and between d1eda80 and baefd08.

📒 Files selected for processing (5)
  • docs/source/features/sampling.md
  • tensorrt_llm/_torch/pyexecutor/llm_request.py
  • tensorrt_llm/_torch/pyexecutor/sampler/sampler.py
  • tensorrt_llm/sampling_params.py
  • tests/unittest/_torch/sampler/test_torch_sampler.py

@QiJune
QiJune requested a review from lori-ren July 23, 2026 01:59
Comment thread tensorrt_llm/_torch/pyexecutor/sampler/sampler.py Outdated
Comment thread tensorrt_llm/_torch/pyexecutor/sampler/sampler.py Outdated
Comment thread tensorrt_llm/_torch/pyexecutor/sampler/sampler.py Outdated
@zhaoyangwang-nvidia
zhaoyangwang-nvidia force-pushed the torch-sampler-no-repeat-ngram branch 3 times, most recently from 7bf7eb4 to 940c68e Compare July 23, 2026 07:13
@zhaoyangwang-nvidia

Copy link
Copy Markdown
Collaborator Author

Hi @lori-ren all comments were addressed, please take a look.

@zhaoyangwang-nvidia
zhaoyangwang-nvidia force-pushed the torch-sampler-no-repeat-ngram branch from 940c68e to 646e258 Compare July 23, 2026 07:14

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
tests/unittest/_torch/sampler/test_torch_sampler.py (2)

2954-3024: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Fresh-path assertions verified correct.

Manually traced the index construction against all bigram/trigram/unigram/incremental/rollback/multi-step assertions in this block — all match the expected _extend_no_repeat_ngram_index semantics.

One gap: test_disabled_request_untouched (Line 2989-2994) only covers ngram_size=None. The SamplingParams.no_repeat_ngram_size contract states both None and 0 disable the restriction — worth adding a 0 case alongside None to lock in that branch, since 0 exercises a different code path (an explicit falsy check) than None.

✅ Suggested additional case
     def test_disabled_request_untouched(self):
         active = self.MockLlmRequest(tokens=[1, 2, 3, 1, 2])
-        disabled = self.MockLlmRequest(tokens=[1, 2, 3, 1, 2])
-        logits = self._run([active, disabled], [2, None])
+        disabled_none = self.MockLlmRequest(tokens=[1, 2, 3, 1, 2])
+        disabled_zero = self.MockLlmRequest(tokens=[1, 2, 3, 1, 2])
+        logits = self._run([active, disabled_none, disabled_zero], [2, None, 0])
         assert self._banned_cols(logits[0]) == {3}
-        assert self._banned_cols(logits[1]) == set()
+        assert self._banned_cols(logits[1]) == set()
+        assert self._banned_cols(logits[2]) == set()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unittest/_torch/sampler/test_torch_sampler.py` around lines 2954 -
3024, Add coverage to test_disabled_request_untouched for an explicitly
configured no_repeat_ngram_size of 0, alongside the existing None case. Verify
the request remains unchanged and no tokens are banned, while preserving the
current enabled-request assertion.

3025-3105: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Stale-host assertions verified correct; multi-beam divergence untested.

Traced the stale/device-boundary math for all 8 stale-path tests (including the subtle "virtual self-transition" case in test_stale_no_nan_on_duplicate_bans) — all check out.

_make_sampler (Line 2926-2937) fixes max_beam_width=1, and no test here passes num_beams > 1 with per-beam-divergent histories. Per _extend_no_repeat_ngram_index's own docstring, the incremental cache is single-beam only "as beam histories diverge," implying a separate/fallback code path handles beam_width > 1. That path isn't exercised in this file, despite the PR description listing "multi-beam layouts" among the 18 covered scenarios.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unittest/_torch/sampler/test_torch_sampler.py` around lines 3025 -
3105, Add coverage for divergent multi-beam histories in the stale-host sampler
tests; current _make_sampler fixes max_beam_width=1 and never exercises the
fallback path. Create a sampler/request setup with num_beams greater than one
and distinct per-beam token histories, then invoke _run_stale or the relevant
sampling flow and assert each beam receives the correct no-repeat-ngram bans.
Ensure the test validates the path used when _extend_no_repeat_ngram_index
cannot use its single-beam incremental cache.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tests/unittest/_torch/sampler/test_torch_sampler.py`:
- Around line 2954-3024: Add coverage to test_disabled_request_untouched for an
explicitly configured no_repeat_ngram_size of 0, alongside the existing None
case. Verify the request remains unchanged and no tokens are banned, while
preserving the current enabled-request assertion.
- Around line 3025-3105: Add coverage for divergent multi-beam histories in the
stale-host sampler tests; current _make_sampler fixes max_beam_width=1 and never
exercises the fallback path. Create a sampler/request setup with num_beams
greater than one and distinct per-beam token histories, then invoke _run_stale
or the relevant sampling flow and assert each beam receives the correct
no-repeat-ngram bans. Ensure the test validates the path used when
_extend_no_repeat_ngram_index cannot use its single-beam incremental cache.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: dcf99299-557a-45d6-b3fa-5a910e2cba56

📥 Commits

Reviewing files that changed from the base of the PR and between 7bf7eb4 and 646e258.

📒 Files selected for processing (5)
  • docs/source/features/sampling.md
  • tensorrt_llm/_torch/pyexecutor/llm_request.py
  • tensorrt_llm/_torch/pyexecutor/sampler/sampler.py
  • tensorrt_llm/sampling_params.py
  • tests/unittest/_torch/sampler/test_torch_sampler.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • tensorrt_llm/sampling_params.py
  • docs/source/features/sampling.md
  • tensorrt_llm/_torch/pyexecutor/sampler/sampler.py

@zhaoyangwang-nvidia
zhaoyangwang-nvidia force-pushed the torch-sampler-no-repeat-ngram branch from dcead0f to 58f6d9d Compare July 23, 2026 08:45

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/unittest/_torch/sampler/test_torch_sampler.py (1)

3002-3007: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Cover the 0 disabled value.

The contract requires both None and 0 to disable the restriction, but this test only exercises None. Add a second disabled request using ngram_size=0 and assert that its logits remain unchanged.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unittest/_torch/sampler/test_torch_sampler.py` around lines 3002 -
3007, Extend test_disabled_request_untouched to include a request configured
with ngram_size=0, run it alongside the existing active and None-disabled
requests, and assert its banned columns are empty. Preserve the existing
assertions for the active request and the None-disabled request.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/unittest/_torch/sampler/test_torch_sampler.py`:
- Around line 2952-2960: Extend the tests around _run and _apply_no_repeat_ngram
with a num_beams=[2] case using distinct token histories for each beam.
Configure the mock request so get_tokens(beam_idx) returns beam-specific
sequences, then assert each logits row is banned only according to its
corresponding beam history.

---

Outside diff comments:
In `@tests/unittest/_torch/sampler/test_torch_sampler.py`:
- Around line 3002-3007: Extend test_disabled_request_untouched to include a
request configured with ngram_size=0, run it alongside the existing active and
None-disabled requests, and assert its banned columns are empty. Preserve the
existing assertions for the active request and the None-disabled request.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: a326faf9-9f4a-43d5-a483-72fee52fadd5

📥 Commits

Reviewing files that changed from the base of the PR and between 646e258 and 58f6d9d.

📒 Files selected for processing (2)
  • tensorrt_llm/_torch/pyexecutor/sampler/sampler.py
  • tests/unittest/_torch/sampler/test_torch_sampler.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tensorrt_llm/_torch/pyexecutor/sampler/sampler.py

Comment thread tests/unittest/_torch/sampler/test_torch_sampler.py Outdated
@zhaoyangwang-nvidia
zhaoyangwang-nvidia force-pushed the torch-sampler-no-repeat-ngram branch from 646e258 to dcead0f Compare July 23, 2026 08:52
Comment thread tensorrt_llm/_torch/pyexecutor/sampler/sampler.py Outdated
Comment thread tensorrt_llm/_torch/pyexecutor/sampler/sampler.py Outdated
Comment thread tensorrt_llm/_torch/pyexecutor/sampler/sampler.py Outdated
Comment thread tensorrt_llm/_torch/pyexecutor/sampler/sampler.py Outdated
Comment thread tensorrt_llm/_torch/pyexecutor/sampler/sampler.py Outdated
Comment thread tensorrt_llm/_torch/pyexecutor/sampler/sampler.py Outdated
Comment thread tensorrt_llm/_torch/pyexecutor/sampler/sampler.py Outdated
Comment thread tensorrt_llm/_torch/pyexecutor/sampler/sampler.py Outdated
Comment thread tensorrt_llm/_torch/pyexecutor/sampler/sampler.py Outdated
Comment thread tests/unittest/_torch/sampler/test_torch_sampler.py Outdated
@zhaoyangwang-nvidia
zhaoyangwang-nvidia requested a review from a team as a code owner July 24, 2026 09:09
@zhaoyangwang-nvidia
zhaoyangwang-nvidia requested a review from a team as a code owner July 24, 2026 09:39
Implement the no_repeat_ngram_size sampling parameter for the PyTorch
TorchSampler (previously honored only by the C++ executor path). A token is
excluded from sampling if it would recreate an n-gram already present in the
sequence (prompt included), matching the C++ banRepeatNgram kernel and HF's
NoRepeatNGramLogitsProcessor. None or 0 disables the restriction.

Token-ban handling is organized into a dedicated token_ban submodule:

- TokenBans: host-side accumulator of (row, col) index lists; all features
  append into one instance and flush with a single H2D transfer + index_put_
  per category.
- TokenBanHandler (base): shared feature logic (bad words, no-repeat ngram,
  min-length EOS suppression, the suffix-rule matcher, the incremental n-gram
  index, and the unconditional flush).
- Two variants selected once from whether the overlap scheduler is enabled:
  SynchronousTokenBanHandler (history always complete, all bans unconditional)
  and OverlappedTokenBanHandler (a batch mixes fresh and stale requests; stale
  ones produce conditional bans resolved on the device without a D2H sync).

bad_words and min-length EOS suppression are folded into the same module and
share the single accumulator/flush. The n-gram matcher keeps an incremental
per-request index (host-side token mirror grown via scalar accessors, avoiding
the ~40us full get_tokens() copy), so the steady-state per-step cost is
O(new tokens); the isolated sampler benchmark shows ~2us n-gram overhead on
H200 (bs=32, 2048-token context, n=3).

Docs and the SamplingParams docstring updated. Unit tests for all three
token-ban paths live in tests/unittest/_torch/sampler/test_token_ban.py.

Signed-off-by: ZhaoyangWang <zhaoyangw@nvidia.com>
@zhaoyangwang-nvidia
zhaoyangwang-nvidia force-pushed the torch-sampler-no-repeat-ngram branch from 81dcd3c to 5f44c53 Compare July 24, 2026 09:50
@zhaoyangwang-nvidia zhaoyangwang-nvidia changed the title [TRTLLM-13233][feat] Support no_repeat_ngram_size in TorchSampler [TRTLLM-13233][feat] Support no_repeat_ngram_size in TorchSampler and refactor token-ban handling into its own submodule Jul 24, 2026
@zhaoyangwang-nvidia

Copy link
Copy Markdown
Collaborator Author

Hi @ixlmar I have refactored all token-ban handling into a new token_ban submodule:

  • TokenBans — a host-side accumulator of the (row, col) ban indices; every feature appends into one instance so all bans flush with a single H2D transfer + index_put_ per category.
  • TokenBanHandler (ABC) — the "few high-level methods" facade: the sampler only calls generate_ban_list() / apply_ban_list(). It holds the shared logic (rule generators, the suffix matcher, the incremental ngram index, and the flush).
  • Two variants selected once at construction from whether the overlap scheduler is enabled — SynchronousTokenBanHandler (all bans unconditional) and OverlappedTokenBanHandler (adds the conditional, device-resolved stale path). Overlap-on/off is a fixed config, so the fresh-vs-stale routing is done by picking the variant rather than branching per call (the overlap variant still handles a fresh/stale mix within a batch, since staleness is per-request).

All three token-ban features — bad words, no-repeat ngram, and the min-length EOS suppression — are now merged into this one handler and share the single accumulator and flush. Token-ban unit tests moved to a dedicated test_token_ban.py. No behavior or measurable performance change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants